package com.naiterui.ehp.bs.ds.modules.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.security.utils.SecurityUtils;
import com.naiterui.ehp.bp.support.exception.BaseExceptionCodes;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bs.ds.common.constants.CacheConstants;
import com.naiterui.ehp.bs.ds.common.utils.Constant;
import com.naiterui.ehp.bs.ds.common.utils.PageVO;
import com.naiterui.ehp.bs.ds.common.vo.PageParamsVO;
import com.naiterui.ehp.bs.ds.modules.goods.vo.WarehouseManageVO;
import com.naiterui.ehp.bs.ds.modules.sys.entity.Dept;
import com.naiterui.ehp.bs.ds.modules.sys.entity.Role;
import com.naiterui.ehp.bs.ds.modules.sys.entity.User;
import com.naiterui.ehp.bs.ds.modules.sys.mapper.UserMapper;
import com.naiterui.ehp.bs.ds.modules.sys.mapper.UserRoleMapper;
import com.naiterui.ehp.bs.ds.modules.sys.service.IDeptService;
import com.naiterui.ehp.bs.ds.modules.sys.service.IRoleService;
import com.naiterui.ehp.bs.ds.modules.sys.service.IUserRoleService;
import com.naiterui.ehp.bs.ds.modules.sys.service.IUserService;
import com.naiterui.ehp.bs.ds.modules.sys.utils.DictUtil;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 系统用户
 */
@Slf4j
@Service
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

  private final IRoleService roleService;
  private final IDeptService deptService;
  private final IUserRoleService userRoleService;
  private final UserRoleMapper userRoleMapper;
  private final PasswordEncoder passwordEncoder;

  @Override
  public List<Long> queryAllMenuId(Long userId) {
    return this.baseMapper.queryAllMenuId(userId);
  }

  @Override
  public User getById(Serializable id) {
    User user = super.getById(id);
    // 获取用户所属的角色列表
    List<Long> roleIdList = this.userRoleMapper.queryRoleIdList(user.getId());
    user.setRoleIdList(roleIdList);
    user.setPassword(null);
    return user;
  }

  @Override
  public PageVO<User> queryPage(PageParamsVO pageParamsVO, User user) {
    QueryWrapper<User> entityWrapper = pageParamsVO.getEntityWrapper(user);
    IPage<User> page = this.page(pageParamsVO.getPage(), entityWrapper);
    page.getRecords().forEach(sysUserEntity -> {
      Long deptId = sysUserEntity.getDeptId();
      if (deptId != null) {
        Dept dept = this.deptService.getById(deptId);
        sysUserEntity.setDeptName(dept.getName());
      }
      sysUserEntity.setPassword(null);
    });
    return PageVO.toPageVo(page);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public boolean save(User user) {
    String salt = RandomStringUtils.randomAlphanumeric(20);
    user.setSalt(salt);
    user.setPassword(this.passwordEncoder.encode(user.getPassword()));
    user.setLastPasswordResetDate(new Date());
    boolean save = super.save(user);
    // 保存用户与角色关系
    this.userRoleService.saveOrUpdate(user.getId(), user.getRoleIdList());
    return save;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  @CacheEvict(value = {CacheConstants.USER}, key = "#user.username", allEntries = true)
  public void update(User user) throws BusinessException {
    Preconditions.checkNotNull(user.getId(), "用户ID不能为null");
    Long warehouseId = user.getWarehouseId();
    if (warehouseId == null) {
      if (SecurityUtils.getCurrentUser().getId() != Constant.SUPER_ADMIN) {
        if (user.getId() == Constant.SUPER_ADMIN) {
          throw new BusinessException(BaseExceptionCodes.FAILED, "您当前权限不够！");
        }
      }
    }
    String password = user.getPassword();
    if (StringUtils.isNotBlank(password)) {
      user.setPassword(this.passwordEncoder.encode(password));
      user.setLastPasswordResetDate(new Date());
    }
    super.updateById(user);
    // 保存用户与角色关系
    this.userRoleService.saveOrUpdate(user.getId(), user.getRoleIdList());
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  @CacheEvict(value = CacheConstants.USER, key = "#user.username", allEntries = true)
  public void updatePassword(User user, String password, String newPassword) throws BusinessException {

    if (!this.passwordEncoder.matches(user.getPassword(), password)) {
      throw new BusinessException(BaseExceptionCodes.FAILED, "原密码不正确，请检查后重新输入");
    }
    String redisKey = CacheConstants.USER_UPDATE_PAWD + user.getId();
    String encodePassword = this.passwordEncoder.encode(newPassword);
    Long length = RedisUtil.listOps().llen(redisKey);
    if (length == null || length <= 0) {
      RedisUtil.listOps().lpush(redisKey, user.getPassword());
    }
    long endIndex = length == null || length <= 1 ? 0 : length - 1;
    List<String> historyPwd = RedisUtil.listOps().lrange(redisKey, 0, endIndex);
    if (CollectionUtils.isNotEmpty(historyPwd)) {
      for (String pwd : historyPwd) {
        if (this.passwordEncoder.matches(pwd, newPassword)) {
          throw new BusinessException(BaseExceptionCodes.FAILED, "系统检测到您近期使用过该密码，为了您的账户安全，请重新设置!");
        }
      }
    }
    user.setPassword(encodePassword);
    user.setLastPasswordResetDate(new Date());
    super.updateById(user);

    //大于五次，只保留最近五次密码校验
    RedisUtil.listOps().lpush(redisKey, encodePassword);
    if (endIndex >= 4) {
      RedisUtil.listOps().rpop(redisKey);
    }
  }

  @Override
  @Cacheable(value = CacheConstants.PERMISSION_CACHE_PREFIX, key = "#userId")
  public Set<String> getPermission(Long userId) {
    User user = this.getById(userId);

    List<String> permissionList = this.baseMapper.queryAllPerms(
        user.getRoleIdList().contains(Constant.SUPER_ADMIN) ? null : userId);

    // 用户权限列表
    Set<String> permissionSet = new HashSet<>();

    permissionList.stream()
        .filter(perms -> !StringUtils.isBlank(perms))
        .map(perms -> Arrays.asList(perms.trim().split(",")))
        .forEach(permissionSet::addAll);
    return permissionSet;
  }

  @Override
  public String getUserNameByWarehouseId(Long warehouseId) {
    User user = this.baseMapper.getByWarehouseId(warehouseId);
    if (user == null) {
      return null;
    }
    return user.getUsername();
  }

  @Override
  @CacheEvict(value = {CacheConstants.USER, CacheConstants.USER_MENU_LIST, CacheConstants.PERMISSION_CACHE_PREFIX}, allEntries = true)
  public void updateManage(Long warehouseId, WarehouseManageVO warehouseManage) throws BusinessException {
    User user = this.baseMapper.getByWarehouseId(warehouseId);
    String roleId = DictUtil.getNameByTypeAndCode("user_manage", "manage");
    if (user == null) {
      String userName = warehouseManage.getUserName();
      if (this.baseMapper.findByUsername(userName) != null) {
        throw new BusinessException(BaseExceptionCodes.FAILED, "商家管理账号不能重复");
      }
      user = new User();
      user.setUsername(userName);
      user.setPassword(warehouseManage.getPassword());
      user.setRoleIdList(Lists.newArrayList(Long.parseLong(roleId)));
      user.setStatus(1);
      user.setMobile(warehouseManage.getPhone());
      user.setWarehouseId(warehouseId);
      user.setName("");
      user.setEmail("");
      this.save(user);
    } else {
      if (warehouseManage.getPassword() != null) {
        user.setPassword(warehouseManage.getPassword());
      }
      user.setRoleIdList(Lists.newArrayList(Long.parseLong(roleId)));
      if (warehouseManage.getPhone() != null) {
        user.setMobile(warehouseManage.getPhone());
      }
      this.update(user);
    }
  }

  @Override
  @CacheEvict(value = {CacheConstants.USER, CacheConstants.USER_MENU_LIST, CacheConstants.PERMISSION_CACHE_PREFIX}, allEntries = true)
  public void updateStatus(Long warehouseId, Integer status) {
    User user = this.baseMapper.getByWarehouseId(warehouseId);
    if (user != null) {
      user.setStatus(status == 0 ? 1 : 0);
      this.baseMapper.updateById(user);
    }
  }

  @Override
  public User loadUserByUsername(String username) throws BusinessException {
    log.info("用户登陆，查询用户信息 username:{}", username);
    if (StringUtils.isBlank(username)) {
      throw new UsernameNotFoundException("用户名为空");
    }
    // 查询用户信息
    User user = this.baseMapper.findByUsername(username);
    if (user == null) {
      throw new UsernameNotFoundException("用户不存在");
    }
    // 获取用户所属的角色列表
    List<Long> roleIdList = this.userRoleService.queryRoleIdList(user.getId());
    user.setRoleIdList(roleIdList);

    if (roleIdList.isEmpty()) {
      throw new BusinessException(BaseExceptionCodes.FAILED, "您没有权限登陆系统,请联系管理员！");
    }

    Dept dept = this.deptService.getById(user.getDeptId());
    if (dept != null) {
      user.setDeptName(dept.getName());
    }

    List<Role> roles = this.roleService.selectBatchIds(roleIdList);
    user.setRoles(roles);

    Set<String> permission = this.getPermission(user.getId());
    user.setPermissions(permission);
    return user;
  }

  @Override
  @CacheEvict(value = {CacheConstants.USER, CacheConstants.USER_MENU_LIST, CacheConstants.PERMISSION_CACHE_PREFIX}, allEntries = true)
  public boolean removeByIds(Collection<? extends Serializable> idList) {
    return super.removeByIds(idList);
  }
}
